<?php
/**
* @author Niek Saarberg <n.saarberg@gmail.com>
* @version 0.0.1
* @package Auth
* @date April 6, 2009
*
* Todo:
* - 1 inlog tegelijkertijd
*
* Changes:
* 0.0.1
* - Class created and added base functionality
*
* Description:
* - Deze class regelt het verkeer tusses Authentication, Authorisation en Auth_Cache
*/


/**
 * Define auth vars
 */
define('AUTH_INACTIVE', 'inactive');
define('AUTH_ACTIVE', 'active');
define('AUTH_REMOVED', 'removed');

/**
 * Max attempts to login
 */
define('AUTH_MAX_ATTEMPTS', 3);
define('AUTH_BAN_EXPIRES', 1800);	// 30 minutes
define('AUTH_LOGIN_URI', 'login');	// note: this string will be called with View::url()

define('AUTH_USER_SESSION', 'Gi_USER_eJ3a');
define('AUTH_PERMS_SESSION', '5t_AUTH_R7eI');
define('AUTH_AGENT_SESSION', '5t_USER_AGENT_R7eI');

class Auth {
	
	/**
	 * Object van de class
	 */
	private static $instance;
	
	/**
	 * Zet de functies van construct en clone op void
	 */
	private function __construct() {}
    private function __clone() {}
 
 	/**
 	 * Maak singleton van de class
	 * 
	 * @return object Self
 	 */
    public static function getInstance() {
    	
        if (!self::$instance instanceof self) {
        	
            self::$instance = new self();
        }
        
        return self::$instance;
    }
    
    /**
     * Check if user has the right permission
     */
    public static function hasPermission($subject, $action = false) {
    	
    	return Auth_cache::getInstance()->get($subject, $action);
    }
    
    /**
     * Try to login the user
     */
    public function login($email, $password) {
    	
    	if($this->isLogged() == true) {
    		
    		return true;
    	}
    	
    	return Authentication::getInstance()->login($email, $password);
    }
    
    /**
     * Kijk alsof gebruiker wel is ingelogd en alsof het echt de gebruiker is.
     */
    public function isLogged() {
    	
    	return Authentication::getInstance()->isLogged();
    }
    
    /**
     * Gebruiker uitloggen
     */
    public function logout() {
    	
    	Authentication::getInstance()->logout();
    }
    
    /**
     * Vraag user data op
     */
    public function get($field = 'name') {
    	
    	$data = Auth_cache::getInstance()->getUser();
    	
    	return (isset($data[$field])) ? $data[$field] : false;
    }
    
    /**
     * Helper functie
     * 
     * Checked alsof de gebruiker is ingelogd, verwijzd hem anders door naar de user controller
     */
    public static function logged($view) {
    	
    	if(Auth::getInstance()->isLogged() == false) {
    		
    		header("Location: " . $view->url('user', 'login'));
    	}
   	}
}

/**
 * Zorgt voor het afhandelen van de inlog
 */
class Authentication {
	
	/**
	 * Object van de class
	 */
	private static $instance;
	
	/**
	 * Session hash voor veiligheid
	 */
	private $ses_hash = "Uas4@hd&@|jRsNB";
	
	/**
	 * Zet de functies van construct en clone op void
	 */
	private function __construct() {}
    private function __clone() {}
 
 	/**
 	 * Maak singleton van de class
	 * 
	 * @return object Self
 	 */
    public static function getInstance() {
    	
        if (!self::$instance instanceof self) {
        	
            self::$instance = new self();
        }
        
        return self::$instance;
    }
    
    public function login($email, $password) {
    	
    	// Nieuwe inlog, oude verwijderen, cache legen enz
    	
    	$db = DBH::getInstance();
    	$hash = array('78x', 'tG8O');
		
		$password = $hash[0] . mysql_real_escape_string($password) . $hash[1];
		$email = mysql_real_escape_string($email);
		
		if($this->hasTry() == false) {
			
			die("Uw ip is een half uur gebanned wegens te veel inlog pogingen.");
		}
		
		$sql = "SELECT `user`.`user_id`, `user`.`name`, `user`.`site_id` FROM `user`" 
			. " WHERE `user`.`mail` = '" . $email . "'"
			. " AND `user`.`password` = '" . md5($password) . "' LIMIT 0,1;";

		$result = $db->query($sql);
		
		if(mysql_num_rows($result) == 1) {
			// Succes
			
			$row = mysql_fetch_assoc($result);
			
			/**
			 * Sla gebruiker op
			 */
			Auth_cache::getInstance()->setUser($row);
			
			/**
			 * Krijg alle gebruiker rechten terug
			 */
			Authorisation::getInstance()->fetch($row['user_id']);
			
			/**
			 * Poging tot iets veiligere sessie
			 */
			$_SESSION[AUTH_AGENT_SESSION] = md5($this->ses_hash . $_SERVER['HTTP_USER_AGENT'] . $this->ses_hash);
			
			return true;
		} else {
			// Failed attempt
			$this->failedAttempt();
		}
		
		return false;
    }
    
    /**
     * Logout
     */
    public function logout() {
    	
    	session_destroy();
    	session_regenerate_id();
    	
    }
    
    /**
     * Check how much tries the user has left to login
     */
    private function hasTry() {
    	
    	$db = DBH::getInstance();
    	
    	$ip = $_SERVER['REMOTE_ADDR'];
    	$time = time();
    	
    	$sql = "SELECT * FROM `login_attempt` WHERE `login_attempt`.`ip` = '{$ip}'";
    	$result = $db->query($sql);
    	
    	if(mysql_num_rows($result) == 0) {
    		// Voeg ip toe
    		
    		$db->insert('login_attempt', array('ip' => $ip, 'time' => $time));
    	} else {
    		
    		$row = mysql_fetch_assoc($result);
    		
    		// Session expired
    		if($row['time'] > ($time - AUTH_BAN_EXPIRES)) {
    			
    			$db->update('login_attempt', array('attempt' => 0), "`ip` = '{$ip}'");
    		}
    		
    		if($row['attempt'] >= AUTH_MAX_ATTEMPTS) {    			
    			
    			return false;
    		}
    	}
    	
    	return true;
    }
    
    /**
     * Add attempt
     */
    private function failedAttempt() {
    	
    	$db = DBH::getInstance();
    	
    	$ip = $_SERVER['REMOTE_ADDR'];
    	
    	$row = $db->fetch_row('login_attempt', "`ip` = '{$ip}'");
    	
    	$db->update('login_attempt', array('attempt' => $row['attempt'] + 1, 'time' => time()), "`ip` = '{$ip}'");
    }
    
    /**
     * Check if user is logged in
     * Check for possible hacking attempts
     *
     * When to use: session_regenerate_id();
     */
    public function isLogged() {
    	
    	/**
    	 * Kijk alsof er een gebruiker is opgeslagen
    	 */
    	if(Auth_cache::getUser() == false) {
    		
    		//$this->logout();
    		return false;
    	}
    	
    	/**
    	 * Against Session Hijacking
    	 */
    	if($_SESSION[AUTH_AGENT_SESSION] != md5($this->ses_hash . $_SERVER['HTTP_USER_AGENT'] . $this->ses_hash)) {
    		
    		//$this->logout();
    		
    		/**
    		 * Log deze gebeurtenis
    		 */
    		return false;
    	}
    	
    	return true;
    }
}

class Authorisation {
	
	/**
	 * Object van de class
	 */
	private static $instance;
	
	/**
	 * Zet de functies van construct en clone op void
	 */
	private function __construct() {}
    private function __clone() {}
 
 	/**
 	 * Maak singleton van de class
	 * 
	 * @return object Self
 	 */
    public static function getInstance() {
    	
        if (!self::$instance instanceof self) {
        	
            self::$instance = new self();
        }
        
        return self::$instance;
    }
	
	public function fetch($user_id) {
		
		$auth_cache = Auth_cache::getInstance();
		
		if($auth_cache->issetCache() == true) {
			
			// Cache has being set
			return;
		}
		
		$db = DBH::getInstance();
		
		$sql = "SELECT `permission`.`subject`, `permission`.`action` FROM `user`"
			. " INNER JOIN `user_group` ON (`user_group`.`user_id` = `user`.`user_id`)"
			. " INNER JOIN `group_permission` ON (`group_permission`.`group_id` = `user_group`.`group_id`)"
			. " INNER JOIN `permission` ON (`permission`.`permission_id` = `group_permission`.`permission_id`)"
			. " WHERE `user`.`user_id` = '{$user_id}'";
			
		$result = $db->query($sql);
		
		/**
		 * Sla rechten op in Auth_cache
		 */
		while($row = mysql_fetch_assoc($result)) {
			
			$auth_cache->$row['subject'] = $row['action'];
		}
	}
}

class Auth_cache {
	
	/**
	 * Object van de class
	 */
	private static $instance;
	
	/**
	 * Sla het subject op
	 */
	private $subject;
	
	/**
	 * Zet de functies van construct en clone op void
	 */
	private function __construct() {}
    private function __clone() {}
 
 	/**
 	 * Maak singleton van de class
	 * 
	 * @return object Self
 	 */
    public static function getInstance() {
    	
        if (!self::$instance instanceof self) {
        	
            self::$instance = new self();
            
            /**
             * Zet de sessie voor cache van gebruikers-rechten
             */
            if(isset($_SESSION[AUTH_PERMS_SESSION]) == false) {
            	
            	$_SESSION[AUTH_PERMS_SESSION] = array();
			}
        }
        
        return self::$instance;
    }
    
    /**
     * Magic set method
     */
    public function __set($label, $value) {
    	
    	if(isset($_SESSION[AUTH_PERMS_SESSION][$label]) == false) {
    		
    		$_SESSION[AUTH_PERMS_SESSION][$label] = array();
    	}
    	
    	$_SESSION[AUTH_PERMS_SESSION][$label][$value] = true;
    }
    
    /**
     * Get method
     */
    public function get($subject, $action) {
    	
    	if(empty($subject)) {
    		
    		throw new Exception("Subject expected.");
    	}
    	
    	/**
    	 * Module recht
    	 */
    	if($action == NULL) {
    		
    		return isset($_SESSION[AUTH_PERMS_SESSION][$subject]);
    	}
    	
    	/**
    	 * Module en actie recht
    	 */
    	//if(isset($_SESSION[AUTH_PERMS_SESSION][$subject][$action]) == false) {
    		
    		return isset($_SESSION[AUTH_PERMS_SESSION][$subject][$action]);
    	//}
    	
    	//return $_SESSION[AUTH_PERMS_SESSION][$subject][$action];
    }
    
    /**
     * checked alsof de cache bestaat
     */
    public function issetCache() {
    	
    	return (isset($_SESSION[AUTH_PERMS_SESSION]) && sizeof($_SESSION[AUTH_PERMS_SESSION]) > 0) ? true : false;
    }
    
    /**
     * Save user data
     */
    public function setUser($data) {
    	
    	$_SESSION[AUTH_USER_SESSION] = $data;
    }
    
    /**
     * Get user data
     */
    public function getUser() {
    	
    	if(isset($_SESSION[AUTH_USER_SESSION]) == true && sizeof($_SESSION[AUTH_USER_SESSION]) > 0) {
    		
    		return $_SESSION[AUTH_USER_SESSION];
    	}
    	
    	return false;
    }
}